﻿using System;
using System.IO;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Xml;
using System.Data;
using System.Data.Common;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Windows.Input;
using System.IO;
using System.Reflection;
using NHunspell;
using dte = EnvDTE;
using System.Windows.Documents;
using System.Text.RegularExpressions;
using System.Diagnostics;

namespace for7raid.VSSpellerAddin
{
    public partial class wSpell
    {

        static string AppPath = Path.GetDirectoryName(
             Assembly.GetExecutingAssembly().CodeBase).Remove(0, 6);
        static string DicPath = AppPath + @"\VSSpellerAddinDic";
        List<Dic> _dics = new List<Dic>();
        Hunspell _hunspell;
        string _text;
        List<WordEntry> _words = new List<WordEntry>();
        List<string> _ignoredWords = new List<string>();
        int _curWordIndex = 0;
        TextRange _textrange;


        public wSpell()
        {
            this.InitializeComponent();
            try
            {
                //Load all dictionaries
                foreach (string d in (Directory.GetFiles(DicPath, "*.dic")))
                {
                    _dics.Add(
                        new Dic()
                        {
                            Name = Path.GetFileNameWithoutExtension(d),
                            File = d,
                            FileAff = Path.ChangeExtension(d, "aff")
                        });
                }
            }
            catch (Exception ex)
            {

                throw;
            }

            try
            {
                Hunspell.NativeDllPath = AppPath;
            }
            catch (Exception ex)
            {
                
                //throw;
            }

            cmdDictionaries.ItemsSource = _dics;
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            cmdDictionaries.SelectedIndex = 0;
            dte.TextSelection tsel = (dte.TextSelection)Connect._applicationObject.ActiveDocument.Selection;
            
            _text = tsel.Text;
            txt.Text = _text;

            ProcessStart();
        }

        /// <summary>
        /// Start new checking process
        /// </summary>
        private void ProcessStart()
        {
            _curWordIndex = 0;
            _ignoredWords.Clear();
            _words.Clear();

            txt.Text = _text;

            SplitToWords();

            ProcessNext();
        }


        /// <summary>
        /// Process the next word in list
        /// </summary>
        private void ProcessNext()
        {
            //skip all correct and ignored words
            while (_curWordIndex < _words.Count && 
                (_hunspell.Spell(_words[_curWordIndex].Word) || _ignoredWords.Contains(_words[_curWordIndex].Word)))
            {
                
                _curWordIndex++;
            }
            if (_curWordIndex < _words.Count)
            {

                WordEntry we = _words[_curWordIndex];
                lstSuggestions.ItemsSource = _hunspell.Suggest(we.Word);
                
                //select incorrect word in text box
                txt.Focus();
                txt.CaretIndex = we.Start;
                txt.Select(we.Start, we.Length);

                if (lstSuggestions.Items.Count > 0)
                    lstSuggestions.SelectedIndex = 0;
                
                //Debug.WriteLine(we.Word + " start: " +  we.Start + " l: " + we.Length );
            }
            else
            {
                AskAndClose();
            }
        }

        private void AskAndClose()
        {
            MessageBoxResult res = MessageBox.Show("The spelling chek is complete.\r\nSave changes and close?",
                    "Visual studio speller addin",
                     MessageBoxButton.YesNoCancel, MessageBoxImage.Question, MessageBoxResult.Yes);
            if (res == MessageBoxResult.Yes)
            {
                Save();
                this.Close();
            }
            else if (res == MessageBoxResult.No)
            {
                this.Close();
            }
            else if (res == MessageBoxResult.Cancel)
            {

                ProcessStart();
            }
        }
        /// <summary>
        /// This function split selected text into array of words and store the start position of them
        /// </summary>
        private void SplitToWords()
        {
            //split words to array
            string[] words = Regex.Split(_text, @"\W+");
            int i = 0;
            foreach (string word in words)
            {
                //skip empty words
                if (string.IsNullOrEmpty(word)) continue;
                //find the start position of word
                i = _text.IndexOf(word, i);
                _words.Add(
                    new WordEntry()
                    {
                        Word = word,
                        Start = i
                    });
                //next search position
                i += word.Length;
            }
        }

        private void cmdDictionaries_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            Dic d = cmdDictionaries.SelectedItem as Dic;
            // _hyphen = new Hyphen(d.File);
            _hunspell = new Hunspell(d.FileAff, d.File);

            //forward back and re-check current word
            if (_curWordIndex >0)
                _curWordIndex--;
            if (!string.IsNullOrEmpty(_text))
                ProcessNext();
        }

        private void Window_Unloaded(object sender, RoutedEventArgs e)
        {
            _hunspell.Dispose();
            // _hyphen.Dispose();
        }

        private void btnIgnoreOnce_Click(object sender, RoutedEventArgs e)
        {
            //just skip current word
           _curWordIndex++;
           ProcessNext();

        }

        private void lstSuggestions_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            ChangeWord();
        }

        private void ChangeWord()
        {
            //change only one word
            string newword = lstSuggestions.SelectedItem as string;
            WordEntry we = _words[_curWordIndex];
            _text = _text.Remove(we.Start, we.Length).Insert(we.Start, newword);

            ProcessStart();

        }

        private void btnChange_Click(object sender, RoutedEventArgs e)
        {
            ChangeWord();
        }

        private void btnAddToDic_Click(object sender, RoutedEventArgs e)
        {
            //add the word into dictionary and re-check text
            WordEntry we = _words[_curWordIndex];
            _hunspell.Add(we.Word);
            ProcessStart();
        }

        private void btnOK_Click(object sender, RoutedEventArgs e)
        {
            Save();
            this.Close();
        }

        private void Save()
        {
            //save the text
            dte.TextSelection tsel = (dte.TextSelection)Connect._applicationObject.ActiveDocument.Selection;
            tsel.Text=_text;
            //format all document while the selection has been reseted :(
            Connect._applicationObject.ExecuteCommand("Edit.FormatDocument","");
        }

        private void btnIgnoreAll_Click(object sender, RoutedEventArgs e)
        {
            if (_curWordIndex < _words.Count)
            {
                //store ignored word for while session
                WordEntry we = _words[_curWordIndex];
                _ignoredWords.Add(we.Word);
                //and continue the checking
                _curWordIndex++;
                ProcessNext();
            }
            else
            {
                AskAndClose();
            }
        }

        private void btnChangeAll_Click(object sender, RoutedEventArgs e)
        {
            //replace all incorrect word in text
            string newword = lstSuggestions.SelectedItem as string;
            WordEntry we = _words[_curWordIndex];
            _text = _text.Replace(we.Word, newword);

            ProcessStart();
        }

        private void txt_KeyUp(object sender, KeyEventArgs e)
        {
            //we can modify text and save changes by Ctrl+Enter
            if (e.Key == Key.Enter && e.KeyboardDevice.Modifiers == ModifierKeys.Control)
            {
                _text = txt.Text;
                ProcessStart();
            }
        }

    }
}